#ifndef JVM_H_
#define JVM_H_

#define HEAPSIZE		1024
#define CODESIZE		4096

#ifdef __KERNEL__
#include <sys/types.h>
#include <mem/MemReader.h>
#else
#define	__packed	__attribute__((__packed__))
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned char u8;
typedef signed char s8;
typedef short s16;
typedef long int s32;
#endif

#include <Object.h>

namespace kernel
{
  namespace virt
  {
    namespace common
    {

      namespace opcodes
      {
        enum
        {
          NOP = 0x1000, MOV, IF, EQ, LT, RETURN, INVOKE,

          ADD, SUB, REM, SHL, SHR, USHR, AND, OR, XOR,

          NEW, ARRAYLENGTH,

        };
      }
    }

    namespace jvm
    {
      typedef union
      {
        s16 w;
        struct
        {
          s8 bl, bh;
        } z;
      } vm_arg_t;

      typedef struct
      {
        int type; /* CONSTANT_Fieldref_info, CONSTANT_Methodref_info, and CONSTANT_InterfaceMethodref_info	*/
        int id0; /* class index in constant pool */
        int id1; /* name index in constant pool */
        int id2; /* descriptor index in constant pool */
        int acc_flags;
        char ref_type;

        int ref_dim; /* reference dimension index */
        union
        {
          char c;
          int i;
          double d;
          float f;
          long l;
          short s;
          void *ref;
        } val;

        int AtomID;
      } field_ref_t;

      typedef struct
      {
        int type; /* CONSTANT_Fieldref_info, CONSTANT_Methodref_info, and CONSTANT_InterfaceMethodref_info	*/
        int id0; /* class index in constant pool */
        int id1; /* name index in constant pool */
        int id2; /* descriptor index in constant pool */
        int acc_flags;
        int ret_type;

        int max_stack;
        int max_locals;
        int max_params;

        int AtomID;
      } code_ref_t;

      typedef struct
      {
        int type; /* CONSTANT_Fieldref_info, CONSTANT_Methodref_info, and CONSTANT_InterfaceMethodref_info	*/
        int id0; /* class index in constant pool */
        int id1; /* index in fields or codes array, depending on entry type */
        int id2; /* class name id */
        int AtomID;
        int backRef; /* back reference to the field or method pool */
      } cp_ref_t;

      /**
       * TODO Create ArrayRef class
       */
      typedef struct array_ref
      {
        struct array_ref *prev;
        struct array_ref *next;
        struct array_ref *lnk;
        int type; /* type of an element */
        int d_size; /* size of an element */
        int d_num; /* number of elements */
        char *data;
      } array_ref_t;
    }
  }
} /* namespace kernel::virt  */

using ::kernel::virt::jvm::cp_ref_t;
using ::kernel::virt::jvm::code_ref_t;
using ::kernel::virt::jvm::field_ref_t;
using ::kernel::virt::jvm::array_ref_t;
using ::kernel::virt::jvm::vm_arg_t;

/**
 *
 */
namespace kernel
{
  namespace virt
  {
    namespace jvm
    {
      namespace opcodes
      {
        enum
        {
          NOP = 0x00, ACONST_NULL, // 0x01
          ICONST_M1, // 0x02
          ICONST_0, // 0x03
          ICONST_1, // 0x04
          ICONST_2, // 0x05
          ICONST_3, // 0x06
          ICONST_4, // 0x07
          ICONST_5, // 0x08
          LCONST_0, // 0x09
          LCONST_1, // 0x0a
          FCONST_0, //   0x0b
          FCONST_1, //   0x0c
          FCONST_2, //   0x0d

          BIPUSH = 0x10, // 0x10
          SIPUSH, //     0x11
          LDC, //        0x12

          ILOAD = 0x15, //      0x15
          FLOAD = 0x17, //     0x17
          DLOAD, //	 0x18
          ALOAD, //	 0x19

          ILOAD_0 = 0x1a, // 0x1a
          ILOAD_1, //    0x1b
          ILOAD_2, //    0x1c
          ILOAD_3, //    0x1d

          FLOAD_0 = 0x22, // 0x22
          FLOAD_1, //    0x23
          FLOAD_2, //    0x24
          FLOAD_3, //    0x25

          ALOAD_0 = 0x2a, //    0x2a
          ALOAD_1, //    0x2b
          ALOAD_2, //    0x2c
          ALOAD_3, //    0x2d
          IALOAD, //     0x2e

          FALOAD = 0x30, // 0x30
          AALOAD = 0x32, // 0x32
          BALOAD, //     0x33
          CALOAD, //     0x34
          SALOAD, //     0x35
          ISTORE, //     0x36

          FSTORE = 0x38, // 0x38

          ASTORE = 0x3a, // 0x3a
          ISTORE_0, //   0x3b
          ISTORE_1, //   0x3c
          ISTORE_2, //   0x3d
          ISTORE_3, //   0x3e

          FSTORE_0 = 0x43, // 0x43
          FSTORE_1, //   0x44
          FSTORE_2, //   0x45
          FSTORE_3, //   0x46

          ASTORE_0 = 0x4b, // 0x4b
          ASTORE_1, //   0x4c
          ASTORE_2, //   0x4d
          ASTORE_3, //   0x4e
          IASTORE, //    0x4f

          FASTORE = 0x51, // 0x51

          AASTORE = 0x53, // 0x53
          BASTORE, //    0x54
          CASTORE, //    0x55
          SASTORE, //    0x56
          POP, //        0x57
          POP2, //       0x58
          DUP, //        0x59
          DUP_X1, //     0x5a
          DUP_X2, //     0x5b
          DUP2, //       0x5c
          DUP2_X1, //    0x5d
          DUP2_X2, //    0x5e
          SWAP, //       0x5f
          IADD, //       0x60

          FADD = 0x62, // 0x62
          ISUB = 0x64, // 0x64
          FSUB = 0x66, // 0x66
          IMUL = 0x68, // 0x68
          FMUL = 0x6a, // 0x6a
          IDIV = 0x6c, // 0x6c
          FDIV = 0x6e, // 0x6e
          IREM = 0x70, // 0x70
          FREM = 0x72, // 0x72
          INEG = 0x74, // 0x74
          FNEG = 0x76, // 0x76
          ISHL = 0x78, // 0x78
          ISHR = 0x7a, // 0x7a
          IUSHR = 0x7c, // 0x7c
          IAND = 0x7e, // 0x7e
          IOR = 0x80, // 0x80
          IXOR = 0x82, // 0x82
          IINC = 0x84, // 0x84

          I2F = 0x86, // 0x86
          F2I = 0x8b, // 0x8b
          I2B = 0x91, // 0x91

          FCMPL = 0x95,// 0x95
          FCMPG = 0x96,// 0x96

          IFEQ = 0x99,// 0x99
          IFNE = 0x9a,// 0x9a
          IFLT = 0x9b,// 0x9b
          IFGE = 0x9c,// 0x9c
          IFGT = 0x9d,// 0x9d
          IFLE = 0x9e,// 0x9e

          IF_ICMPEQ = 0x9f, //  0x9f
          IF_ICMPNE = 0xa0, //  0xa0
          IF_ICMPLT = 0xa1,//  0xa1
          IF_ICMPGE = 0xa2,//  0xa2
          IF_ICMPGT = 0xa3,//  0xa3
          IF_ICMPLE = 0xa4,//  0xa4

          GOTO = 0xa7,//       0xa7

          TABLESWITCH = 0xaa,//0xaa
          LOOKUPSWITCH = 0xab,// 0xab

          IRETURN = 0xac,//    0xac
          LRETURN = 0xad,//    0xad
          FRETURN = 0xae,//    0xae
          DRETURN = 0xaf,//    0xaf
          ARETURN = 0xb0,//    0xb0
          RETURN = 0xb1,//     0xb1

          GETSTATIC = 0xb2,//  0xb2
          PUTSTATIC = 0xb3,//  0xb3
          GETFIELD = 0xb4,//  0xb4
          PUTFIELD = 0xb5,//   0xb5
          INVOKEVIRTUAL = 0xb6,// 0xb6
          INVOKESPECIAL = 0xb7,// 0xb7
          INVOKESTATIC = 0xb8,// 0xb8

          NEW = 0xbb,//        0xbb
          NEWARRAY = 0xbc,//   0xbc
          ANEWARRAY = 0xbd,//  0xbd
          ARRAYLENGTH = 0xbe,//0xbe

          VSUM = 0xcb, //       0xcb	/* average scalar multiplication */
          VSUB = 0xcc,
        //  0xcc	/* average vector multiplication */
        };
      }

      namespace fieldtypes
      {
        enum
        {
          B = 'B', /* byte */
          C = 'C', /* unicode char */
          D = 'D', /* double */
          F = 'F', /* float */
          I = 'I', /* int */
          J = 'J', /* long */
          L = 'L', /* class reference */
          S = 'S', /* short */
          Z = 'Z', /* boolean */
          REF = '[', FIELD, METHOD, OPCODE,
        /* reference */
        };
      }

      namespace vartypes
      {
        enum
        {
          BOOLEAN = 4, /* boolean */
          CHAR, FLOAT, DOUBLE, BYTE, SHORT, INT, LONG,
        };
      }

      namespace consttypes
      {
        enum
        {
          INTEGER = 3, //		3
          FLOAT = 4, //		4
          LONG = 4, //		4
          STRINGREF = 8, //	8
          FIELDREF,
          METHDREF,
          IFACEREF,
        };
      }

      namespace bits
      {
        enum
        {
          ACC_PUBLIC = 0x0001, /* Declared public, may be accessed from outside its package. */
          ACC_PRIVATE = 0x0002, /* Declared private, accessible only within the defining class. */
          ACC_PROTECTED = 0x0004, /* Declared protected, may be accessed within subclasses. */
          ACC_STATIC = 0x0008, /* Declared static. */
          ACC_FINAL = 0x0010, /* Declared final, no subclasses allowed. */
          ACC_SUPER = 0x0020, /* Treat superclass methods specially when invoked by the invokespecial instruction. */
          ACC_VOLATILE = 0x0040, /*	Declared volatile, cannot be cached. */
          ACC_TRANSIENT = 0x0080, /* Declared transient, not written or read by a persistent object manager. */
          ACC_SYNCHRONIZED = 0x0020, /* Declared synchronized, invocation is wrapped in a monitor lock. */
          ACC_INTERFACE = 0x0200, /* Is an interface, not a class. */
          ACC_ABSTRACT = 0x0400, /* Declared abstract, may not be instantiated.  */
          ACC_NATIVE = 0x0100, /* Declared native, implemented in a language other than Java. */
          ACC_STRICT = 0x0800,
        /* Declared strictfp, floating-point mode is FP-strict */
        };
      }

      /**
       * This might be atomic object of jvm
       */
      class IAtom : public Object
      {
      public:
        int ID; // Id of an atom (Its 'name')
        int l; // left index
        int r; // right index
        int c; // atom links weight: negative - for input links, positive for output

      public:
        //
        IAtom() :
          l(0), r(0), c(0)
        {
        }
        //
        IAtom(int _l, int _r) :
          l(_l), r(_r), c(0)
        {
        }
        //
        ~IAtom()
        {
        }

        // get atoms by their index
        IAtom *
        getLeft()
        {
          return (IAtom *) l;
        }
        IAtom *
        getRight()
        {
          return (IAtom *) r;
        }

        // Arrays must be organized as BST
        IAtom *
        getById(int id)
        {
          IAtom *ret = 0;
          if (ID == id)
            return this;
          else if (l && (ret = ((IAtom *) l)->getById(id)) != 0)
            return ret;
          else if (r)
            return ((IAtom *) r)->getById(id);
          else
            return 0;
        }

        // swaps l and r
        void
        swap(void)
        {
          int tmp = r;
          r = l;
          l = tmp;
        }

        //
        virtual int
        getVal() = 0;
        virtual void
        setVal(int _v) = 0;
      };

      /**
       * This might be atomic object
       * of jvm semantic trees
       */
      class VAtom : public IAtom
      {
      public:
        int v; // value

      public:
        //
        VAtom()
        {
        }
        //
        VAtom(int _l, int _r, int _v) :
          IAtom(_l, _r), v(_v)
        {
        }
        //
        ~VAtom()
        {
        }

        virtual int
        getVal()
        {
          return v;
        }
        virtual void
        setVal(int _v)
        {
          v = _v;
        }
      };

      /**
       * This is BST or 'dynamic array' class
       * An information tree representation.
       */
      class DemandTree : public IAtom
      {
      public:
        int v; // value
        int depth; // tree depth

      public:
        //
        DemandTree() :
          depth(0)
        {
        }
        //
        DemandTree(int _l, int _r, int _v) :
          IAtom(_l, _r), v(_v)
        {
        }
        //
        ~DemandTree()
        {
        }

        virtual void
        setVal(int _v)
        {
          v = _v;
        }

        // recursive function!
        virtual int
        getVal(void)
        {
          return v;
        }

        DemandTree *
        getLeft(void)
        {
          return (DemandTree *) l;
        }

        DemandTree *
        getRight(void)
        {
          return (DemandTree *) r;
        }

        /*
         * Get reference
         */
        DemandTree *
        getById(unsigned int id)
        {
          int dir; // direction

          // out of bounds
          if ((1 << depth) < id)
            return 0;

          // am I terminal node
          if (!depth)
            if (id)
              return getRight();
            else
              return getLeft();

          // If internal node
          if ((id & (1 << depth))) // get right
            if (r)
              {
                id &= ((1 << depth) - 1);
                return getRight()->getById(id);
              }
            else
              return 0;
          else if (l)
            {
              id &= ((1 << depth) - 1);
              return getLeft()->getById(id);
            }
          else
            return 0;
        }

        /*
         * Get reference
         */
        DemandTree *
        createId(unsigned int id)
        {
          DemandTree *dt;
          int d = sizeof(unsigned int) * 8; // direction

          if (!id)
            return this;

          // if out of bounds
          if ((1 << depth) < id)
            // get an 'id' depth
            while (!(id & (1 << --d)))
              ;

          dt = new DemandTree();
          // copy myself to the left subtree
          dt->l = l;
          dt->r = r;
          dt->depth = depth;
          l = (int) dt;

          // create new right subtree
          dt = new DemandTree();
          r = (int) dt;

          // increase my depth
          this->depth = d;

          /*
           * .....
           */

        }

      };

      /**
       *  Atomic class of jvm
       */
      class AtomDetector : public Object
      {
      public:
        int v; // value
        int *uplink;

      public:
        bool
        operator ==(int i)
        {
          return (v == i);
        }
      };

      /**
       *
       */
      class Stack : public Object
      {
        unsigned int stack[128];
        unsigned int *sp; // stack pointer

      public:
        Stack()
        {
          sp = stack;
        }

        ~Stack()
        {
        }

        //
        void
        push(unsigned int val)
        {
          *(++sp) = val;
        }
        //
        unsigned int
        pop(void)
        {
          return *(sp--);
        }
      };

      //
      class Jvm : public Object
      {
      public:
        Jvm();
        Jvm(char *);
        ~Jvm();
        int
        parseByteCode();
        void
        parseCode(char *pc, int len, int *refs);
        void
        runAtoms(int ID);

      private:
        int
        new_array(int len);
        static int
        parse_max_paramnum(char *s, int len);
        void
        push(int);
        int
        pop(void);
        int
        putfield(int, int);
        int
        getfield(int idx);
        void
        reset(void);

      public:
        /**/
        IReader *clReader;

      public:
        /* Instantiated class state */
        int stack[128];
        cp_ref_t *constPool;
        code_ref_t *funcPool;
        field_ref_t *fieldPool;
        array_ref_t *arrayList;
        // Stack stk;

        // current atom in chain
        int curAtom;

        /* current stack frame and registers */
        u8 *pc; // Program counter
        int *sp; // stack pointer
        int mID; // running method reference ID

      };

    }
  }
}

#endif /*JVM_H_*/
